package bma.common.netty;

import java.io.File;
import java.net.ServerSocket;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
import org.jboss.netty.handler.codec.http.HttpClientCodec;
import org.jboss.netty.handler.codec.http.HttpContentDecompressor;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.util.CharsetUtil;
import org.junit.Test;

import bma.common.langutil.ai.stack.AIStackSimple;
import bma.common.langutil.concurrent.TimerManager;
import bma.common.langutil.core.ObjectUtil;
import bma.common.langutil.io.HostPort;
import bma.common.langutil.io.IOUtil;
import bma.common.langutil.log.LogPrinter.LEVEL;
import bma.common.langutil.pipeline.CommonPipelineBuilder;
import bma.common.netty.client.CommonNettyClientSender;
import bma.common.netty.client.NettyClient;
import bma.common.netty.client.NettyClientBootstrap;
import bma.common.netty.client.NettyClientCloseListener;
import bma.common.netty.handler.ChannelHandlerLog;
import bma.common.netty.handler.ChannelHandlerLog.TYPE;

public class NettyClientTC {

	final org.slf4j.Logger log = org.slf4j.LoggerFactory
			.getLogger(NettyClientTC.class);

	protected NettyClientBootstrap boot() {
		NettyClientBootstrap b = new NettyClientBootstrap();
		b.init();
		return b;
	}

	public NettyClient baidu80(
			final CommonPipelineBuilder<ChannelHandler> builder)
			throws Exception {
		NettyClientBootstrap b = boot();

		HostPort host = new HostPort("www.baidu.com", 80);
		ChannelPipelineFactory fac = new ChannelPipelineFactory() {

			@Override
			public ChannelPipeline getPipeline() throws Exception {
				ChannelPipeline p = Channels.pipeline();
				p.addLast("log", new ChannelHandlerLog(log, LEVEL.DEBUG,
						TYPE.ALL, 128));
				if (builder != null) {
					builder.buildPipeline(new NettyChannelPipeline(p));
				}
				return p;
			}
		};

		AIStackSimple<NettyClient> stack = new AIStackSimple<NettyClient>(null);
		b.connect(stack, host.createInetSocketAddress(), null, fac);

		return stack.get();
	}

	public NettyClient jk80(final CommonPipelineBuilder<ChannelHandler> builder)
			throws Exception {
		NettyClientBootstrap b = boot();

		HostPort host = new HostPort("111.178.146.54", 80);
		ChannelPipelineFactory fac = new ChannelPipelineFactory() {

			@Override
			public ChannelPipeline getPipeline() throws Exception {
				ChannelPipeline p = Channels.pipeline();
				p.addLast("log", new ChannelHandlerLog(log, LEVEL.DEBUG,
						TYPE.ALL, 128));
				if (builder != null) {
					builder.buildPipeline(new NettyChannelPipeline(p));
				}
				return p;
			}
		};

		AIStackSimple<NettyClient> stack = new AIStackSimple<NettyClient>(null);
		b.connect(stack, host.createInetSocketAddress(), null, fac);

		return stack.get();
	}

	@Test
	public void nettyClient_base() throws Exception {
		NettyClient c = baidu80(null);
		log.info("CLIENT = {}", c);
		c.close();
	}

	@Test
	public void nettyClient_write() throws Exception {
		NettyClient c = baidu80(null);
		c.write(ChannelBuffers
				.wrappedBuffer("GET /\r\nHost: www.baidu.com\r\n\r\n"
						.getBytes()));
		ObjectUtil.waitFor(this, 2000);
		log.info("end");
		c.close();
	}

	@Test
	public void nettyClient_jkapi() throws Exception {

		File f = new File("e:/tmp/p1.dat");
		String s = IOUtil.readFileToString(f, "UTF-8");
		String s2 = IOUtil.readFileToString(new File("e:/tmp/p2.dat"), "UTF-8");

		System.out.println(s.equals(s2));

		String bound = "491299511942";
		String nbound = "X" + System.currentTimeMillis();
		nbound = bound;
		s = s.replace(bound, nbound);

		ChannelBuffer cb = ChannelBuffers.dynamicBuffer(1024);
		for (int i = 0; i < s.length(); i++) {
			char c1 = s.charAt(i);
			if (c1 == '\\') {
				char c2 = s.charAt(i + 1);
				if (c2 != 'x')
					throw new IllegalStateException("index " + i);
				c2 = s.charAt(i + 2);
				char c3 = s.charAt(i + 3);
				StringBuilder b = new StringBuilder();
				b.append(c2).append(c3);
				int v = Integer.parseInt(b.toString(), 16);
				cb.writeByte(v);
				i += 3;
			} else {
				cb.writeByte(c1);
			}
		}
		int len = cb.writerIndex();

		StringBuilder sb2 = new StringBuilder(1024);
		Map<String, String> ps = new HashMap<String, String>();
		ps.put("type", "t1");
		for (Map.Entry<String, String> e : ps.entrySet()) {
			sb2 = sb2.append("--");
			sb2 = sb2.append(nbound);
			sb2 = sb2.append("\r\n");
			sb2 = sb2.append("Content-Disposition: form-data; name=\""
					+ e.getKey() + "\"\r\n\r\n");
			sb2 = sb2.append(URLEncoder.encode(e.getValue(), "UTF-8"));
			sb2 = sb2.append("\r\n");
		}
		sb2.append("--" + nbound + "--\r\n");
		len = sb2.toString().getBytes().length;

		NettyClient c = jk80(null);
		StringBuilder sb = new StringBuilder();
		sb.append("POST /api/monitorUploadPic.php?uid=1 HTTP/1.1\r\n");
		sb.append("Host: ljk.live.yy.com\r\n");
		sb.append("Content-Length: ").append(len).append("\r\n");
		sb.append("Content-Type: multipart/form-data; boundary=")
				.append(nbound).append("\r\n");
		sb.append("\r\n");

		System.out.println(sb);
		System.out.println(sb2);
		// if(sb.length()>0)return;

		c.write(ChannelBuffers.wrappedBuffer(sb.toString().getBytes()));
		c.write(ChannelBuffers.wrappedBuffer(sb2.toString().getBytes()));
		ObjectUtil.waitFor(this, 2000);
		c.close();
	}

	public class HttpResponseHandler extends SimpleChannelUpstreamHandler {

		private boolean readingChunks;

		@Override
		public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
				throws Exception {
			if (!readingChunks) {
				HttpResponse response = (HttpResponse) e.getMessage();

				System.out.println("STATUS: " + response.getStatus());
				System.out.println("VERSION: " + response.getProtocolVersion());
				System.out.println();

				if (!response.getHeaderNames().isEmpty()) {
					for (String name : response.getHeaderNames()) {
						for (String value : response.getHeaders(name)) {
							System.out.println("HEADER: " + name + " = "
									+ value);
						}
					}
					System.out.println();
				}

				if (response.isChunked()) {
					readingChunks = true;
					System.out.println("CHUNKED CONTENT {");
				} else {
					ChannelBuffer content = response.getContent();
					if (content.readable()) {
						System.out.println("CONTENT {");
						System.out.println(content.toString(CharsetUtil.UTF_8));
						System.out.println("} END OF CONTENT");
					}
				}
			} else {
				HttpChunk chunk = (HttpChunk) e.getMessage();
				if (chunk.isLast()) {
					readingChunks = false;
					System.out.println("} END OF CHUNKED CONTENT");
				} else {
					System.out.print(chunk.getContent().toString(
							CharsetUtil.UTF_8));
					System.out.flush();
				}
			}
		}
	}

	@Test
	public void nettyClient_addHandler() throws Exception {
		NettyClient c = baidu80(null);
		if (c.missHandler("codec")) {
			c.addHandler("codec", new HttpClientCodec());
		}
		if (c.missHandler("inflater")) {
			c.addHandler("inflater", new HttpContentDecompressor());
		}
		if (c.missHandler("handler")) {
			c.addHandler("handler", new HttpResponseHandler());
		}

		HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
				HttpMethod.GET, "/");
		request.setHeader(HttpHeaders.Names.HOST, "www.baidu.com");
		request.setHeader(HttpHeaders.Names.CONNECTION,
				HttpHeaders.Values.CLOSE);
		request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING,
				HttpHeaders.Values.GZIP);

		c.write(request);
		ObjectUtil.waitFor(this, 2000);
		c.close();
	}

	@Test
	public void nettyClient_reqresp() throws Exception {
		NettyClient c = baidu80(null);
		c.addHandler("codec", new HttpClientCodec());
		c.addHandler("inflater", new HttpContentDecompressor());
		c.addHandler("aggregator", new HttpChunkAggregator(1024 * 1024 * 10));

		final HttpRequest request = new DefaultHttpRequest(
				HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
		request.setHeader(HttpHeaders.Names.HOST, "www.baidu.com");
		request.setHeader(HttpHeaders.Names.CONNECTION,
				HttpHeaders.Values.KEEP_ALIVE);
		request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING,
				HttpHeaders.Values.GZIP);

		AIStackSimple<HttpResponse> stack = null;
		for (int i = 0; i < 2; i++) {
			stack = new AIStackSimple<HttpResponse>(null);
			c.send(new CommonNettyClientSender<HttpResponse>(stack) {

				@Override
				public boolean messageReceived(NettyClient client,
						Object message) {
					if (message instanceof HttpResponse) {
						HttpResponse rep = (HttpResponse) message;
						end(client, rep, null);
						return true;
					}
					return false;
				}

				@Override
				public void sendRequest(NettyClient client) {
					client.write(request);
				}

			});
		}
		HttpResponse rep = stack.get();
		log.info("RESPONSE = {}", rep);

		c.close();
	}

	@Test
	public void nettyClient_timeout() throws Exception {
		NettyClient c = baidu80(null);
		log.info("CLIENT = {}", c);

		TimerManager tm = new TimerManager();
		tm.startManager();

		AIStackSimple<HttpResponse> stack = new AIStackSimple<HttpResponse>(
				null);
		c.send(new CommonNettyClientSender<HttpResponse>(stack, 2000, tm) {

			@Override
			public boolean messageReceived(NettyClient client, Object message) {
				if (message instanceof HttpResponse) {
					HttpResponse rep = (HttpResponse) message;
					end(client, rep, null);
					return true;
				}
				return false;
			}

			@Override
			public void sendRequest(NettyClient client) {

			}

		});
		HttpResponse rep = stack.get();
		log.info("RESPONSE = {}", rep);

		c.close();
	}

	@Test
	public void nettyClient_closeEvent() throws Exception {

		NettyClientBootstrap b = boot();

		HostPort host = new HostPort("www.baidu.com", 80);
		ChannelPipelineFactory fac = new ChannelPipelineFactory() {

			@Override
			public ChannelPipeline getPipeline() throws Exception {
				ChannelPipeline p = Channels.pipeline();
				p.addLast("log", new ChannelHandlerLog(log, LEVEL.DEBUG,
						TYPE.ALL, 128));
				return p;
			}
		};

		AIStackSimple<NettyClient> stack = new AIStackSimple<NettyClient>(null);
		b.connect(stack, host.createInetSocketAddress(), null, fac);

		NettyClient c = stack.get();

		c.addListener(new NettyClientCloseListener() {

			@Override
			public void clientClose(NettyClient client, boolean remoteClose) {
				System.out.println("close ==> " + remoteClose);
			}
		});
		c.write(ChannelBuffers
				.wrappedBuffer("GET /\r\nHost: www.baidu.com\r\n\r\n"
						.getBytes()));
		ObjectUtil.waitFor(this, 5 * 1000);
		c.close();
	}

	@Test
	public void nettyClient_connectTimeout() throws Exception {

		NettyClientBootstrap b = boot();
		b.setConnectTimeout(10000);
		ChannelPipelineFactory fac = new ChannelPipelineFactory() {

			@Override
			public ChannelPipeline getPipeline() throws Exception {
				ChannelPipeline p = Channels.pipeline();
				p.addLast("log", new ChannelHandlerLog(log, LEVEL.DEBUG,
						TYPE.ALL, 128));
				return p;
			}
		};

		HostPort host = new HostPort("127.0.0.1", 8123);
		AIStackSimple<NettyClient> stack = new AIStackSimple<NettyClient>(null);
		log.info("start connect");
		b.connect(stack, host.createInetSocketAddress(), null, fac);

		try {
			NettyClient c = stack.get();
			c.close();
		} catch (Exception e) {
			log.info("connect fail", e);
		}
	}

}
